/**
 * Class local
 * 
 * @author Hussein Patwa
 * @date 10 April 2008 - Rev 02 May 2008
 * @version 3.0 Implementation of local trending
 */
package trend;

import java.util.*;
import java.lang.Double;

@SuppressWarnings("all")
public class Local {
  /**
   * Constructor for local
   * 
   * @param posTrend Container for positive trends
   * @param negTrend Container for negative trends
   * @param paths Container for paths
   * @param minLength Minimum length after which trend is considered
   *          valid
   * @param maxLength Maximum length after which trend is considered
   *          final
   * @param data Container for originating data
   * @param path Container for the current path being analysed
   */
  HashSet<ArrayList> posTrend = new HashSet();
  HashSet<ArrayList> negTrend = new HashSet();
  Stack<ArrayList> paths;// = new HashSet(); // set

  int minlength;
  int maxlength;
  ArrayList DATA;

  // ArrayList path;

  class observation {
    /**
     * Constructor for an observation
     * 
     * @param dist Distance from X co-ordinate
     * @param diff Distance from Y co-ordinate
     */
    double dist;
    double diff;

    /**
     * Observation
     * 
     * @param dist Distance from X co-ordinate
     * @param diff Distance from Y co-ordinate
     */
    observation(double dist, double diff) {
      this.dist = dist;
      this.diff = diff;
    }
  }

  /**
   * Initialise parameters for local trending
   * 
   * @param neighbourInfo Neighbourhood cluster information
   * @param data Originating data
   * @param index Index number of the current map position
   * @param minLength Minimum length after which trend is considered
   *          valid
   * @param maxLength Maximum length after which trend is considered
   *          final
   * @param min_conf Minimum confidence value
   */
  public Local(NeighbourhoodInfo neighbourInfo, ArrayList data,
      int index, int minLength, int maxLength, double min_conf) {
    DATA = data;
    minlength = minLength;
    maxlength = maxLength;
    paths = computeAllPaths(neighbourInfo, index);
    System.out.println("Number of Paths: " + paths.size());
    // for(ArrayList path:paths){
    // System.out.println(path.size());
    // }
    findTrends(index, data, min_conf, neighbourInfo);
  }

  /**
   * Compute all paths from the given neighbourhood information
   * 
   * @param neighbourInfo Neighborhood information
   * @param index Index of the current map position
   * @return hashset
   */
  Stack computeAllPaths(NeighbourhoodInfo neighbourInfo, int index) {
    Stack rtn = new Stack();
    // Stack rtn1 = new Stack();
    Vector tempNeighbours = neighbourInfo
        .getNeighbourhoodAreaIDs(index);
    // System.out.println("index: " + index + " neighbours : "
    // + tempNeighbours);
    for (Iterator i = tempNeighbours.iterator(); i.hasNext();) {
      Integer node = (Integer) i.next();
      ArrayList tempPath = new ArrayList();
      tempPath.add(new Integer(index));
      tempPath.add(new Integer(node));
      rtn.add(tempPath);
      // System.out.println(tempPath);
    }
    for (int j = 0; j < minlength - 1; j++)
      rtn.addAll(extendAllPaths(rtn, neighbourInfo));
    return rtn;
  }

  /**
   * Extend the current path with more observations
   * 
   * @param paths List of paths to be investigated
   * @param neighbourInfo Neighbourhood information
   * @return hashset
   */
  Stack extendAllPaths(Stack paths, NeighbourhoodInfo neighbourInfo) {
    Stack rtn = new Stack();
    while (!paths.isEmpty()) {
      ArrayList path = (ArrayList) paths.pop();
      int index = ((Integer) path.get(path.size() - 1)).intValue();
      Vector tempNeighbours = neighbourInfo
          .getNeighbourhoodAreaIDs(index);
      // System.out.println("index: " + index + " neighbours: "
      // + tempNeighbours);
      for (Iterator i = tempNeighbours.iterator(); i.hasNext();) {
        Integer node = (Integer) i.next();
        if (!path.contains(node)) {
          ArrayList tempPath = new ArrayList();
          tempPath.addAll(path);
          tempPath.add(node);
          rtn.add(tempPath);
          // System.out.println(tempPath);
        }
      }
    }
    return rtn;
  }

  Stack extendPath(ArrayList path, NeighbourhoodInfo neighbourInfo) {
    Stack rtn = new Stack();
    int index = ((Integer) path.get(path.size() - 1)).intValue();
    Vector tempNeighbours = neighbourInfo
        .getNeighbourhoodAreaIDs(index);
    // System.out.println("index: " + index + " neighbours: "
    // + tempNeighbours);
    for (Iterator i = tempNeighbours.iterator(); i.hasNext();) {
      Integer node = (Integer) i.next();
      if (!path.contains(node)) {
        ArrayList tempPath = new ArrayList();
        tempPath.addAll(path);
        tempPath.add(node);
        rtn.add(tempPath);
        // System.out.println(tempPath);
      }
    }

    return rtn;
  }

  /**
   * Find trends in the originating data - Separate based on
   * positive/negative trend and insert into corresponding sets based on
   * correlation > 0 or < 0
   * 
   * @param index Index of the current position
   * @param data Originating data use to find trends
   * @param min_conf Confidence value for trend alignment
   */
  public void findTrends(int index, ArrayList data, double min_conf,
      NeighbourhoodInfo neighbourInfo) {
    while (!paths.isEmpty()) {
      ArrayList path = (ArrayList) paths.pop(); // Move 1st element of
      // paths to path
      // System.out.println(path.size());
      if ((path.size() >= minlength)) {// &&(path.size()<=maxlength)){
        TreeMap PathObs = new TreeMap();
        for (int j = 0; j < path.size(); j++) {

          Integer currentIndex = (Integer) path.get(j);
          Spatialobject o = (Spatialobject) data.get(index);
          Spatialobject object = (Spatialobject) data.get(currentIndex);
          double dist = Math.sqrt(Math.pow((object.getX() - o.getX()),
              2)
              + Math.pow((object.getY() - o.getY()), 2)); // calculate
          // difference
          // in X
          // co-ordinate
          double diff = object.getV() - o.getV(); // calculate
          // difference
          // in y
          // co-ordinate
          // observation obsTemp = new
          // observation(dist,diff); //
          PathObs.put(dist, diff);
        }
        if (PathObs.size() > 0) {
          Regression reg = new Regression(PathObs);

          // Correlation
          Correlation corr = new Correlation(PathObs);
          double cor = corr.getCorrelationCoefficient();

          if (Math.abs(cor) > min_conf) {
            // System.out.println("PathObs: " + PathObs);
            // System.out.println("Cor: " + cor);
            if (cor > 0) {
              posTrend.add(path);
            } else if (cor < 0) {
              negTrend.add(path);
            }
            if (path.size() < maxlength + 2)
              paths.addAll(extendPath(path, neighbourInfo));
          }
        }
      }
    }
  }

  /**
   * Print identified trends
   */
  public void viewTrends() {
    System.out.println("Positive Trends: ");
    System.out.println("---------------------------------------------");
    for (ArrayList<Integer> p : posTrend) {

      for (Integer index : p) {
        Spatialobject o = (Spatialobject) DATA.get(index);
        System.out.print(o.getName() + "-> ");
      }
      System.out.println();
    }
    System.out.println("Negative Trends: ");
    System.out.println("---------------------------------------------");
    for (ArrayList<Integer> n : negTrend) {

      for (Integer index : n) {
        Spatialobject o = (Spatialobject) DATA.get(index);
        System.out.print(o.getName() + "-> ");
      }
      System.out.println();
    }
  }

}
